#ifndef KERNEL_H
#define KERNEL_H
#include "global.h"

class MsgEnv{
    public:
        int sender_pid;
        int target_pid;
        int num_clock_ticks;
        int type; //use number to represent the type of message
        MsgEnv * next; //link to the next message envelope
        char msg_data[Max_Env_Data];
        MsgEnv();
};

class MsgEnvQ{
    private:
        
        
    public:
        int size;
        MsgEnv * head;
        MsgEnv * tail;
        MsgEnvQ();
        MsgEnvQ(int NumOfNode);
        int enqueue(MsgEnv * newMsgEnv);
        MsgEnv * dequeue();
        int getsize();//count number of nodes in the queue
};
/*
class CompareTime {
    public:
    bool operator()(MsgEnv * left, MsgEnv * right) // Returns true if left<=right
    {
       if (left->num_clock_ticks <= right->num_clock_ticks) return true;
       return false;
    }
};
*/
class PCB{
    public:
        int pid; //process ID
        int priority;
        int status;   
        MsgEnvQ * msgQ; //local message envelope queue
        PCB * next; //link to other PCB
        void (*address)();//pointer point to the process function
        ucontext_t proContext;
        char stack [StackSize];
        //Stack * stack; //stack pointer
        //jmp_buf procContext;//jump buffer for setjmp, longjmp
        PCB();
};

class PCBQ{
    private:
        
        PCB * tail;
        int size;
    public:
        PCB * head;
        PCBQ();
        int enqueue(PCB * newPCB);
        PCB * dequeue();
        PCB * remqueue(int target_pid);
        int getsize();//count number of nodes in the queue
        int get_head_id();
        int get_tail_id();
};

struct TraceBuffer{
    int target_id;
    int sender_id;
    int msg_type;
    int time_stamp;
};

struct initTable {
    int pid;
    int priority;
    int status;
    //int stack_size;
    void (*start_address)();
};

//Kernal Class
class RTX{
    private:
        //current process pointer
        			  		
        PCB * PCB_list[Num_Proc];
        MsgEnvQ * free_env_Q;
        
        
        
        //std::priority_queue<MsgEnv *, std::vector<MsgEnv *>, CompareTime> timeout_list;
        int system_time;
        int atomic_flag;
        TraceBuffer * send_trace_buffer;
        TraceBuffer * receive_trace_buffer;
        
        int k_send_message( int dest_process_id, MsgEnv *msg_envelope );
        MsgEnv * k_receive_message( );
        MsgEnv * k_request_msg_env( );
        int k_release_msg_env ( MsgEnv * memory_block );
        int k_release_processor( );
        int k_request_process_status( MsgEnv * memory_block );
        int k_terminate();
        int k_change_priority(int new_priority, int target_process_id);
        int k_request_delay ( int time_delay, int wakeupcode, MsgEnv *message_envelope );
        int k_send_console_chars(MsgEnv * message_envelope );
        int k_get_console_chars(MsgEnv * message_envelope );
        int k_get_trace_buffers( MsgEnv * message_envelope);
    public:
        MsgEnvQ * timeout_list;
        PCBQ * ready_Q[Num_PriorityLevels];
        PCBQ * blk_on_resource_Q[Num_PriorityLevels];
        MsgEnvQ * Display_Q;
        
        //PCBQ * blk_on_receive_Q[Num_PriorityLevels];
        PCB * cur_proc;
        PCB * prev_proc;
        int clock_display;
        
        RTX();
        //initialization function which sets up variables,
        //queues, pointers, objects and initialize processes
        void initialization(initTable init_table[]);        

        /*dequeue enqueue functions*/
        void rpq_enqueue(PCB* pcb);
        PCB* rpq_dequeue();
        int rpq_getsize();
        PCB* rpq_remqueue(PCB * pcb);
        int blkq_enqueue(PCB * pcb);
        PCB* blkq_dequeue();
        PCB* blkq_remqueue(PCB * pcb);
        int blkq_getsize();
        //void blkq_enqueue(int blocked_type,PCB * pcb);
        //PCB* blkq_dequeue(int blocked_type);        
        //PCB* blkq_remqueue(int blocked_type, PCB * pcb);
        //int blkq_getsize(int blocked_type);
        
        void timeoutq_enqueue(MsgEnv * newNode);//new
        MsgEnv* timeoutq_dequeue();
        MsgEnv* get_timeoutq_head();
        MsgEnv* get_timeoutq_tail();
        
        
        /*other useful functions*/
        PCB * get_PCB(int target_pid);
        void addTrace(MsgEnv* msg_env,int type);//sent=0, recieved=1
        void set_clock(int h, int m, int s);
        void clock_increment();
        int get_time();
        void process_switch();
        void context_switch(PCB * prev_proc,PCB * cur_proc);
        void atomic(bool on);
                
        //user API for real-time kernel
        int send_message( int dest_process_id, MsgEnv *msg_envelope );
        MsgEnv * receive_message( );
        MsgEnv * request_msg_env( );
        int release_msg_env ( MsgEnv * memory_block );
        int release_processor( );
        int request_process_status( MsgEnv * memory_block );
        int terminate();
        int change_priority(int new_priority, int target_process_id);
        int request_delay ( int time_delay, int wakeupcode, MsgEnv *message_envelope );
        int send_console_chars(MsgEnv * message_envelope );
        int get_console_chars(MsgEnv * message_envelope );
        int get_trace_buffers( MsgEnv * message_envelope);
};
void kbd_handler(int signum);
void crt_handler(int signum);
void timer_handler(int signum);;
void kbd_iproc();
void crt_iproc();
void timer_iproc();
void Null_Process();
void Clock_Process();
void Command_interpreter();


void cleanup();
void die(int signal);

/*    
class ReadyQ{
    private:
        PCBQ * pcb_Q[Num_PriorityLevels];//PCB queues
    public:
        ReadyQ();
        ~ReadyQ();
//return total number of PCBs in the ready queue
int getSize();
//return total number of PCBs in the selected priority ready queue
int getSize(int priority);
void enqueue(PCB * newpcb, int priority);
PCB * dequeue();//dequeue and return the PCB with the
*/

/*
why is blocked_on_receive queue redundant??
and still need to add 4 priority levels for the blocked_on_resource queue 
class BlockedQ{
		private:
				 Queue<PCB> pcbQ[Num_BlockedTypes];//PCB queues
				 PCB * tail;
				 PCB * head;
		public:
				 BlockedQ();
				 ~BlockedQ();
				 //return total number of blocked PCBs
				 int getSize();
				 //return total number of PCBs in the selected type queue
				 int getSize(int priority);
				 void enqueue(PCB * newpcb, int blockedType);
				 PCB * dequeue(int blockedtype);
				 int empty();
};
class ReadyQ{
private:
Queue<PCB> pcbQ[Num_PriorityLevels];//PCB queues
PCB * head;
PCB * tail;
public:
ReadyQ();
~ReadyQ();
//return total number of PCBs in the ready queue
int getSize();
//return total number of PCBs in the selected priority
ready queue
int getSize(int priority);
void enqueue(PCB * newpcb, int priority);
PCB * dequeue();//dequeue and return the PCB with the
*/

/*
//not tested yet
class Stack{
		private:
				 int size;
				 int top;
				 char data;
		public:
				 Stack(int size);//malloc space for data
				 int pop();
				 void push(int * item);
};
*/
#endif
